import datetime
import time
from decimal import Decimal

import pandas as pd
from pyecharts.commons.utils import JsCode
from pyecharts.components import Table

from config import ORIGIN_BALANCE, NORMALIZATION
from utils.dataArrange.painter import BaseDraw, DrawError
from typing import *
from pyecharts.charts import Kline, Line, Page, Bar, Grid
from pyecharts.charts.chart import Base, Chart
from pyecharts.charts.mixins import ChartMixin
import pyecharts.options as opts
from globalVar import GlobalVar

signal_map: dict = {
    0: '平',
    1: '多',
    -1: '空'
}


class FinalReport:

    def __init__(self, name: str = None):
        if name is None:
            name = f'report-{datetime.datetime.now().strftime("%Y_%m_%d-%H_%M_%S")}'
        self.infos: Dict[str, Any] = {}
        self.info_row: List[Any] = []
        self.tab = BaseDraw(name)
        self.charts: Dict[str, ChartMixin] = {}

    @staticmethod
    def withdrawal(data: Sequence[Union[int, float]]) -> Tuple[int, int, Union[int, float], List[Any]]:
        """
        计算回撤
        :param data:
        :return:
        """
        df = pd.DataFrame()
        df['_funding'] = data
        df['_maximum'] = df['_funding'].cummax().fillna(method='bfill')
        df['_withdrawal'] = df['_funding'] / df['_maximum'] - 1
        max_draw = - round(df['_withdrawal'].min(), 2)
        max_ = df.loc[df['_withdrawal'] == df['_withdrawal'].min(), '_maximum'].values[0]
        s_idx = df[df['_funding'] == max_].index[0]
        e_idx = df[df['_withdrawal'] == df['_withdrawal'].min()].index[-1]

        return s_idx, e_idx, max_draw, df['_withdrawal'].values.tolist()

        # high = data[0]
        # low = data[0]
        # start_index = 0
        # end_index = 0
        # # 高点
        # high_index = 0
        # # 低点
        # low_index = 0
        # # 最大回撤候选
        # max_decrease: List[Union[int, float]] = [0]
        # # 回撤
        # decrease: List[Union[int, float]] = [0]
        # for i, item in enumerate(data[1:]):
        #     decrease.append(max([0, round(1 - item / max(data[: i + 1]), 2)]))
        #     i += 1
        #     if item > high:
        #         high, low = item, item
        #         high_index, low_index = i, i
        #     elif item >= low:
        #         pass
        #     else:
        #         low = item
        #         low_index = i
        #     dec = round(1 - low / high, 2)
        #     history_dec = max(max_decrease)
        #     if dec > history_dec:
        #         start_index, end_index = high_index, low_index
        #     max_decrease.append(max(dec, history_dec))
        # return start_index, end_index, max(max_decrease), decrease

    def cta_infos(self, x: Sequence[Any], funding: Sequence[Any], df: pd.DataFrame, factors: Sequence[str] = None):
        """
        cta 计算周期信息
        :param funding: 资金
        :param x: x轴
        :param df: 包含信号的K线数据
        :param factors: 需要显示的因子名
        :return:
        """
        # grid = Grid(init_opts=opts.InitOpts(width='100%'))
        kline = Kline(init_opts=opts.InitOpts(width='100%'))
        kline.add_xaxis(x)
        kline.add_yaxis('标的', df.loc[:, ['open', 'close', 'low', 'high']].__array__().tolist(),
                        itemstyle_opts=opts.ItemStyleOpts(
                            color="#00da3c",
                            color0="#ec0000",
                        ),
                        markpoint_opts=opts.MarkPointOpts(
                            data=[opts.MarkPointItem(name='买点', coord=[x[i], df['close'].values[i]],
                                                     value=signal_map[s],
                                                     itemstyle_opts=opts.ItemStyleOpts(
                                                         color="#00da3c" if s == 1 else (
                                                             'blue' if s == 0 else '#ec0000'),
                                                     ))
                                  for i, s in enumerate(df['signal']) if s >= -1])
                        )
        kline.extend_axis(
            yaxis=opts.AxisOpts(type_='value', name_location='end')
        )
        kline.set_global_opts(
            xaxis_opts=opts.AxisOpts(is_scale=True),
            yaxis_opts=opts.AxisOpts(
                name='标的价格',
                is_scale=True,
                splitarea_opts=opts.SplitAreaOpts(),
            ),
            datazoom_opts=opts.DataZoomOpts(),
            title_opts=opts.TitleOpts(title='信号点'),
            tooltip_opts=opts.TooltipOpts(trigger='axis', axis_pointer_type='cross'),
            legend_opts=opts.LegendOpts(selected_map={'funding': False})
        )
        line = Line()
        line.add_xaxis(x)
        line.add_yaxis('funding', funding, yaxis_index=1, is_symbol_show=False)
        if factors is not None:
            for i, factor in enumerate(factors):
                # line.extend_axis(yaxis=opts.AxisOpts(name='因子值', type_='value', name_location='end', offset=i * 40))
                line.add_yaxis(factor, df[factor].tolist(), yaxis_index=1, is_symbol_show=False)
        kline.overlap(line)
        self.charts['信号点'] = kline

    def account_funding(self, x: Sequence[Any], name: str, value: Sequence[Any], target: Sequence[Any] = None,
                        net_income: Sequence[Any] = None, normalization: bool = NORMALIZATION) -> None:
        """
        主图
        :param x: x轴数据
        :param name:
        :param value: 资金值
        :param target: 标的价格走势
        :param net_income: 净收益
        :param normalization: 是否归一化
        :return:
        """
        if normalization:
            net_income = [round(x / value[i], 2) for i, x in enumerate(net_income)] if net_income else None
            value = [round(x / value[0], 2) for x in value]
            target = [round(x / target[0], 2) for x in target] if target is not None else None
        chart = Line(init_opts=opts.InitOpts(width='100%'))
        chart.add_xaxis(x)

        chart.add_yaxis(name, value, yaxis_index=0, is_symbol_show=False)
        # 计算回测
        # withdrawals = self.withdrawal(value)
        ser = pd.Series(value)
        withdrawals: pd.Series = 1 - ser / ser.cummax().fillna(0)
        chart.add_yaxis('回撤', withdrawals.round(4).values.tolist(),
                        yaxis_index=1,
                        is_symbol_show=False,
                        areastyle_opts=opts.AreaStyleOpts(opacity=0.5),
                        linestyle_opts=opts.LineStyleOpts(opacity=0))

        chart.extend_axis(
            yaxis=opts.AxisOpts(name='回撤', is_inverse=True, max_=1, type_='value', offset=0 if normalization else 80,
                                name_location='start'),
        )
        if target is not None:
            chart.add_yaxis('标的', target, yaxis_index=0 if normalization else 2, is_symbol_show=False)
            if not normalization:
                chart.extend_axis(
                    yaxis=opts.AxisOpts(
                        name='标的价格',
                        type_='value',
                        position='right'
                    )
                )
        # 净收益
        if net_income is not None:
            bar = Bar()
            # bar.add_xaxis(x)
            bar.add_yaxis('净收益', net_income, yaxis_index=0 if normalization else 3,
                          label_opts=opts.LabelOpts(is_show=False),
                          itemstyle_opts=opts.ItemStyleOpts(
                              color=JsCode("""
                                     function(params) {
                                         if (params.value > 0) {
                                             return '#00FF00';  
                                         } else {
                                             return '#FF0000';  
                                         }
                                     }""")
                          ))
            if not normalization:
                chart.extend_axis(
                    yaxis=opts.AxisOpts(name='净收益', type_='value', position='right', offset=160,
                                        axisline_opts=opts.AxisLineOpts(linestyle_opts=opts.LineStyleOpts(width=1)))
                )
            chart.overlap(bar)

        # chart.set_series_opts(markarea_opts=opts.MarkAreaOpts(
        #     data=[opts.MarkAreaItem(name=f"{name}:{withdrawals[2]}", x=(x[withdrawals[0]], x[withdrawals[1]]),
        #                             value_index=(0, 0))]
        # ))
        chart.set_global_opts(

            xaxis_opts=opts.AxisOpts(is_scale=True, min_=0),
            yaxis_opts=opts.AxisOpts(
                name='资金',
                is_scale=True,
                splitarea_opts=opts.SplitAreaOpts(),
            ),
            datazoom_opts=opts.DataZoomOpts(),
            title_opts=opts.TitleOpts(title='资金曲线'),
            tooltip_opts=opts.TooltipOpts(trigger='axis', axis_pointer_type='cross',
                                          formatter=JsCode(
                                              """
                                              function (params) {
                                              var target = null;
                                              var funding = null;
                                              var strategyRate = null;
                                              var targetRate = null;
                                              var net_income = null;
                                              var withdrawal = null;
                                              for (let item of params) {
                                                if (item['seriesName'] === '%s') {
                                                funding = item.value[1].toFixed(2);
                                                strategyRate = (funding / %s - 1) * 100;
                                                }
                                                if (item['seriesName'] === '标的') {
                                                target = item.value[1].toFixed(2);
                                                targetRate = (target / %s -1) * 100;
                                                }
                                                if (item['seriesName'] === '净收益'){
                                                net_income = item.data;
                                                }
                                                if (item['seriesName'] === '回撤'){
                                                withdrawal = item.value[1];
                                                }
                                              }
                                              
                                              var results = params[0].name + '<br>';
                                              if (typeof target != 'undefined' && target !== null) {
                                                results += '标的价格:' + target + '<br>' + '标的增值:' + targetRate.toFixed(2) + '%%' + '<br>';
                                              }
                                              if (typeof funding != 'undefined' && funding !== null) {
                                                results += '策略资产:' + funding + '<br>' + '策略收益率:' + strategyRate.toFixed(2) + '%%' + '<br>';
                                              }
                                              if (typeof net_income != 'undefined' && net_income !== null) {
                                                results += '净收益:' + net_income.toFixed(2) + '<br>';
                                              }
                                              if (typeof withdrawal != 'undefined' && withdrawal !== null) {
                                                results += '回撤:' + withdrawal.toFixed(4) * 100 + '%%' + '<br>';
                                              }
                                              return results;
                                              }
                                              """ % (name, 1 if normalization else value[0], target[0])
                                          )),
            legend_opts=opts.LegendOpts(selected_map={'净收益': False, '回撤': False})
        )
        self.charts['资金曲线'] = chart

    @staticmethod
    def simple_line(x: Sequence[Any], y: Dict[str, Sequence[Any]], title: str,
                    title_opt: Dict[str, str] = None) -> Chart:
        if title_opt is None:
            title_opt = {}
        chart = Line()
        chart.add_xaxis(x)
        for k, v in y.items():
            chart.add_yaxis(k, v, is_smooth=True)
        chart.set_series_opts(label_opts=opts.LabelOpts(is_show=False))
        chart.set_global_opts(
            title_opts=opts.TitleOpts(title=title, **title_opt),
            xaxis_opts=opts.AxisOpts(is_scale=True),
            yaxis_opts=opts.AxisOpts(
                is_scale=True,
                splitarea_opts=opts.SplitAreaOpts(),
            ),
            # datazoom_opts=opts.DataZoomOpts(type_="inside"),
            tooltip_opts=opts.TooltipOpts(trigger='axis', axis_pointer_type='cross')
        )
        return chart

    def trade_info(self, x: Sequence[Any], spot: Sequence[Any], swap: Sequence[Any], balance: Sequence[Any],
                   unused_funding: Sequence[Any] = None, spot_premium: Sequence[Any] = None,
                   swap_premium: Sequence[Any] = None, ):
        """
        交易信息整理
        :param x: x轴
        :param spot: 现货交易额
        :param swap: 合约交易额
        :param balance: 资金曲线
        :param unused_funding: 未使用资金
        :param spot_premium: 现货手续费
        :param swap_premium: 合约手续费
        :return:
        """
        bar = Bar(init_opts=opts.InitOpts(width='100%'))
        line = Line()

        bar.add_xaxis(x)
        bar.add_yaxis('spot', spot, stack='0', label_opts=opts.LabelOpts(is_show=False))
        bar.add_yaxis('swap', swap, stack='0', label_opts=opts.LabelOpts(is_show=False))
        if unused_funding is not None:
            bar.add_yaxis('unusedFunding', unused_funding, stack='0', label_opts=opts.LabelOpts(is_show=False))

        line.add_xaxis(x)
        line.add_yaxis('balance', balance, is_symbol_show=False)
        if swap_premium is not None and spot_premium is not None:
            bar.extend_axis(yaxis=opts.AxisOpts(
                name='premium', type_='value', position='right'
            ))
            line.add_yaxis('spot手续费', spot_premium, yaxis_index=1, is_symbol_show=False)
            line.add_yaxis('swap手续费', swap_premium, yaxis_index=1, is_symbol_show=False)
        bar.set_global_opts(
            xaxis_opts=opts.AxisOpts(is_scale=True),
            yaxis_opts=opts.AxisOpts(
                is_scale=True,
                splitarea_opts=opts.SplitAreaOpts(),
            ),
            datazoom_opts=opts.DataZoomOpts(),
            title_opts=opts.TitleOpts(title='交易量统计'),
            tooltip_opts=opts.TooltipOpts(trigger='axis', axis_pointer_type='cross'),
            legend_opts=opts.LegendOpts(selected_map={'spot手续费': False, 'swap手续费': False})
        )
        bar.overlap(line)
        self.charts['交易量'] = bar

    def info_table(self, data: Mapping[str, Any]):
        for k, v in data.items():
            self.infos[k] = v

    def generate(self, path: str):
        info_table = Table()
        info_table.add(list(self.infos.keys()), [list(self.infos.values())] + self.info_row)
        self.tab.draw(info_table)
        for k, v in self.charts.items():
            self.tab.draw(v)
        self.tab.dump(path)


if __name__ == '__main__':
    print(FinalReport.withdrawal([1, 1, 1, 0.6, 0.88, 1.34, 1.56, 0.98, 0.90, 1.2]))
